using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Common.Shared.Xml.Wddx;
using Common.Shared.Design;

namespace Common.Shared.Reflection {
	internal static class MemberUtils {

		/// <summary>
		/// Gets the property.
		/// </summary>
		/// <param name="obj">The obj.</param>
		/// <param name="name">The name.</param>
		/// <returns></returns>
		public static PropertyInfo GetProperty ( object obj, string name ) {
			return obj.GetType ( ).GetProperty ( name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase );
		}

		/// <summary>
		/// Gets the properties.
		/// </summary>
		/// <param name="obj">The obj.</param>
		/// <returns></returns>
		public static List<PropertyInfo> GetProperties ( object obj ) {
			List<PropertyInfo> piList = new List<PropertyInfo> ( );
			piList.AddRange ( obj.GetType ( ).GetProperties ( BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase ) );
			return piList;
		}

		public static List<FieldInfo> GetFields ( object obj ) {
			List<FieldInfo> fiList = new List<FieldInfo> ( );
			fiList.AddRange ( obj.GetType ( ).GetFields( BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase ) );
			return fiList;
		}

		/// <summary>
		/// Gets the properties with specified attribute.
		/// </summary>
		/// <typeparam name="T">Teh attribute type</typeparam>
		/// <param name="obj">The obj.</param>
		/// <returns></returns>
		public static List<PropertyInfo> GetPropertiesWithAttribute<T> ( object obj ) where T: Attribute {
			List<PropertyInfo> piList = new List<PropertyInfo> ( );
			foreach ( PropertyInfo pi in GetProperties ( obj ) ) {
				T attr = AttributeUtils.GetAttribute<T> ( pi );
				if ( attr != null && !attr.Equals ( default ( T ) ) ) {
					piList.Add ( pi );
				}
			}
			return piList;
		}

		public static List<FieldInfo> GetFieldsWithAttribute<T> ( object obj ) where T : Attribute {
			List<FieldInfo> fiList = new List<FieldInfo> ( );
			foreach ( FieldInfo fi in GetFields ( obj ) ) {
				T attr = AttributeUtils.GetAttribute<T> ( fi );
				if ( attr != null && !attr.Equals ( default ( T ) ) ) {
					fiList.Add ( fi );
				}
			}
			return fiList;
		}

		public static List<MemberInfo> GetPublicFieldsAndProperties<T> ( ) {
			return GetPublicFieldsAndProperties ( typeof ( T ) );
		}

		public static List<MemberInfo> GetPublicFieldsAndProperties ( Type t ) {
			List<MemberInfo> miList = new List<MemberInfo> ( t.GetFields ( BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.FlattenHierarchy ) );
			miList.AddRange ( t.GetProperties ( BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.FlattenHierarchy ) );
			miList.Sort ( new WddxOrderAttributeComparer ( ) );
			return miList;
		}

		public static List<MemberInfo> GetPublicMemberInfos<T> ( ) {
			return GetPublicMemberInfos ( typeof ( T ) );
		}

		public static List<MemberInfo> GetPublicMemberInfos ( Type t ) {
			List<MemberInfo> miList = new List<MemberInfo> ( t.GetMembers ( BindingFlags.Public | BindingFlags.Instance ) );
			miList.Sort ( new WddxOrderAttributeComparer ( ) );
			return miList;
		}

		public static List<MemberInfo> GetMembersWithAttribute<T> ( Type t ) where T : Attribute {
			List<MemberInfo> miList = new List<MemberInfo> ( );
			foreach ( MemberInfo mi in MemberUtils.GetPublicMemberInfos ( t ) ) {
				T attr = AttributeUtils.GetAttribute<T> ( mi );
				if ( attr != null && !attr.Equals ( default ( T ) ) ) {
					miList.Add ( mi );
				}
			}

			miList.Sort ( new WddxOrderAttributeComparer ( ) );
			return miList;
		}


		public static string GetMemberInfoStringValue ( object obj, MemberInfo mi ) {
			if ( mi is FieldInfo ) {
				return ( mi as FieldInfo ).GetValue ( obj ).ToString ( );
			} else if ( mi is PropertyInfo ) {
				return ( mi as PropertyInfo ).GetValue ( obj, null ).ToString ( );
			} else {
				return string.Empty;
			}
		}

		public static WddxObject GetMemberInfoValue ( object obj, MemberInfo mi ) {
			return GetMemberInfoValue<WddxObject> ( obj, mi );
		}

		public static T GetMemberInfoValue<T> ( object obj, MemberInfo mi ) {
			if ( mi is FieldInfo ) {
				return (T)( mi as FieldInfo ).GetValue ( obj );
			} else if ( mi is PropertyInfo ) {
				return (T)( mi as PropertyInfo ).GetValue ( obj, null );
			} else {
				return default ( T );
			}
		}

		public static Type GetMemberInfoType ( MemberInfo mi ) {
			if ( mi is FieldInfo ) {
				return ( mi as FieldInfo ).FieldType;
			} else if ( mi is PropertyInfo ) {
				return ( mi as PropertyInfo ).PropertyType;
			} else {
				return null;
			}
		}

		public static bool IsCollection ( MemberInfo mi ) {
			Type t = GetMemberInfoType ( mi );
			return t.IsGenericType && t.GetGenericTypeDefinition ( ).Equals ( typeof ( List<> ) );
		}

		public static MethodInfo GetMethod<T> ( T obj, string methodName ) {
			return typeof ( T ).GetMethod ( methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase );
		}

		
	}
}
